<template>
  <div ref="rootEl">Loading...</div>
</template>

<script setup>
import { ref, reactive, onMounted, onBeforeUnmount, watch } from 'vue'
import { useRouter } from 'vue-router';
import qs from 'qs'

// ========== Props ==========
const props = defineProps({
  schema: {
    type: Object,
    default: () => ({
      type: 'page',
      body: 'Hello World!',
    }),
  },
  locals: {
    type: Object,
    default: () => ({}),
  },
  propsData: {
    type: Object,
    default: () => ({}),
  },
  env: {
    type: Object,
    default: () => ({}),
  },
})

// ========== Emits ==========
const emit = defineEmits(['ready'])

// ========== Refs ==========
const rootEl = ref(null)
const router = useRouter()

// ========== Data ==========
const state = reactive({
  context: {
    siteName: 'AMIS DEMO',
  },
  get location() {
    const current = router.currentRoute.value
    return {
      pathname: current.path,
      hash: current.hash,
      query: current.query,
      search: `?${qs.stringify(current.query)}`,
    }
  },
  loading: false,
  amisInstance: null,
  unmounted: false,
})

// ========== Load SDK ==========
function loadScript(src, callback) {
  const script = document.createElement('script')
  script.src = src
  script.onload = () => callback()
  script.onerror = () => callback(new Error(`Failed to load ${src}`))
  document.body.appendChild(script)
}

function loadStyles(styles) {
  for (const path of styles) {
    const style = document.createElement('link')
    style.rel = 'stylesheet'
    style.type = 'text/css'
    style.href = path
    document.head.appendChild(style)
  }
}

function loadSDK() {
  return new Promise((resolve, reject) => {
    if (window.amisRequire) {
      resolve()
      return
    }
    loadStyles([
      '/amis/sdk/sdk.css',
      '/amis/sdk/helper.css',
      '/amis/sdk/iconfont.css',
    ])
    loadScript('/amis/sdk/sdk.js', (err) => {
      if (err) {
        reject(err)
        return
      }
      resolve()
    })
  })
}

// ========== Methods ==========
function updateProps() {
  if (!state.amisInstance) return
  state.amisInstance.updateProps({
    data: { ...props.locals },
    context: state.context,
    ...props.propsData,
  })
}

// ========== Lifecycle ==========
onMounted(async () => {
  try {
    state.loading = true
    await loadSDK()
  } finally {
    state.loading = false
  }

  if (state.unmounted) return

  const scoped = amisRequire('amis/embed')
  const { normalizeLink } = amisRequire('amis-core')

  const instance = scoped.embed(
    rootEl.value,
    props.schema,
    {
      data: { ...props.locals },
      context: state.context,
      location: state.location,
      ...props.propsData,
      vm: {
        router
      }
    },
    {
      jumpTo: (to, action) => {
        if (to === 'goBack') {
          return router.go(-1)
        }

        to = normalizeLink(to, state.location)

        if (action?.actionType === 'url') {
          action.blank === false ? router.push(to) : window.open(to)
          return
        }

        if (action && to && action.target) {
          window.open(to, action.target)
          return
        }

        if (/^https?:\/\//.test(to)) {
          window.location.replace(to)
        } else {
          router.push(to)
        }
      },

      updateLocation: (location, replace) => {
        if (location === 'goBack') {
          return router.go(-1)
        }

        location = normalizeLink(location, state.location)
        replace ? router.replace(location) : router.replace(location)
      },

      ...props.env,
    },
    () => {
      emit('ready', { instance })
    }
  )

  state.amisInstance = instance
})

onBeforeUnmount(() => {
  state.unmounted = true
  if (state.amisInstance?.unmount) {
    state.amisInstance.unmount()
  }
})

// ========== Watchers ==========
watch(
  () => props.locals,
  () => {
    updateProps()
  }
)

watch(
  () => props.propsData,
  () => {
    updateProps()
  }
)

watch(
  () => router.currentRoute.value,
  () => {
    updateProps()
  }
)
</script>